package com.sunny.design.singleton;

import com.sunny.design.singleton.container.ContainerPojo;
import com.sunny.design.singleton.container.ContainerSingleton;
import com.sunny.design.singleton.hungry.HungrySingleton;
import com.sunny.design.singleton.lazy.LazyDoubleLockCheckSingleton;
import com.sunny.design.singleton.lazy.LazyInnerClassSingleton;
import com.sunny.design.singleton.registry.EnumSingleton;
import com.sunny.design.singleton.thread.ThreadLocalExecutor;
import com.sunny.design.singleton.thread.ThreadSingleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
 * @author Sunny
 * @date 2019/12/29 15:59
 */
public class SingletonTest {
    public static void main(String[] args) {
        containerSingleton();
//        threadSingletonTest();
//        singletonTest(5, "com.sunny.design.design.singleton.container.ContainerPojo");

//        reflectSingleton("com.sunny.design.design.singleton.hungry.HungrySingleton", HungrySingleton.getInstance());

//        serializerSingletonTest(SerializerSingleton.getInstance());
    }

    public static void containerSingleton() {
        Thread thread1 = new Thread(() -> {
            Object bean = ContainerSingleton.getBean(ContainerPojo.class.getName());
            System.out.println(Thread.currentThread().getName() + "---" + bean);
        });
        Thread thread2 = new Thread(() -> {
            Object bean = ContainerSingleton.getBean(ContainerPojo.class.getName());
            System.out.println(Thread.currentThread().getName() + "---" + bean);
        });

        thread1.start();
        thread2.start();
    }

    /**
     * 各线程唯一单例测试
     */
    public static void threadSingletonTest() {
        System.out.println(Thread.currentThread().getName() + ": " + ThreadSingleton.getInstance());
        System.out.println(Thread.currentThread().getName() + ": " + ThreadSingleton.getInstance());
        System.out.println(Thread.currentThread().getName() + ": " + ThreadSingleton.getInstance());
        System.out.println(Thread.currentThread().getName() + ": " + ThreadSingleton.getInstance());
        System.out.println(Thread.currentThread().getName() + ": " + ThreadSingleton.getInstance());

        ThreadLocalExecutor thread1 = new ThreadLocalExecutor();
        ThreadLocalExecutor thread2 = new ThreadLocalExecutor();

        thread1.start();
        thread2.start();
    }

    /**
     * 多线程测试单例
     */
    public static void singletonTest(int type, String beanName) {
        for (int i = 0; i < 5; i++) {
            Object object = null;
            switch (type) {
                case 1:
                    object = HungrySingleton.getInstance();
                    break;
                case 2:
                    object = LazyInnerClassSingleton.getInstance();
                    break;
                case 3:
                    object = LazyDoubleLockCheckSingleton.getInstance();
                    break;
                case 4:
                    object = EnumSingleton.getInstance();
                    break;
                case 5:
                    object = ContainerSingleton.getBean(beanName);
                    break;
            }
            Thread thread = new ExecutorThread(object);
            thread.start();
        }
    }

    /**
     * 代码层面的单例没问题,但是可以通过反射破坏单例的唯一性
     * <p>
     * 如果要杜绝这种方式,可以在私有的构造中判断,如果已经存在实例,就抛出异常
     * 杜绝调用者使用反射手段创建单例
     *
     * @param className 需要反射创建的类全限定名
     * @param object    通过正常途径创建的单例对象
     */
    public static void reflectSingletonTest(String className, Object object) {
        try {
            //通过反射破坏单例
            Class<?> clazz = Class.forName(className);
            Constructor<?> constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);

            Object instance = constructor.newInstance();

            System.out.println(instance);
            System.out.println(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过序列化方式破坏单例
     * <p>
     * 如果要杜绝这种方式,可以在类中加入readResolve()方法,返回单例对象
     * <p>
     * 原因是在于ObjectInputStream调用readObject方法时,底层会判断对象是否有重写这个方法
     * 如果没有,则会创建新对象
     * 如果有,则直接返回该方法的结果
     */
    public static void serializerSingletonTest(Object object) {
        Object o1 = null;
        Object o2 = object;

        try {
            FileOutputStream out = new FileOutputStream("Serializer.obj");
            ObjectOutputStream oos = new ObjectOutputStream(out);
            oos.writeObject(o2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("Serializer.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            o1 = ois.readObject();
            ois.close();
            System.out.println(o1);
            System.out.println(o2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
